#ifndef _COMPILATION_PRINT_TASK_H_
#define _COMPILATION_PRINT_TASK_H_

#include <compilation/task/Task.h>

#include <cassert>
#include <iostream>
#include <list>

#include <compilation/task/Handler.h>
#include <compilation/task/Propagator.h>

#include <compilation/Function.h>


// PrintTask and PrintAgenda.
namespace compilation
{

template <InstructionType> struct PrintHandler;

class PrintTask
    : public VirtualTask
{
public:

  int m_indent;

public:

  PrintTask(const VirtualInstruction& instruction)
    : m_indent(0) { setInstruction(instruction); }

  PrintTask(const PrintTask& source)
    : VirtualTask(source), m_indent(source.m_indent) {}

  virtual void applyOn(VirtualInstruction& instruction, VirtualAgenda& continuations);

  virtual VirtualTask* clone(void) const;

  void indent(std::ostream *stream, unsigned offset = 0);
};

class PrintAgenda
    : public VirtualAgenda
{
public:

  typedef PrintTask Task;

  typedef InstructionVisitor<
  void, PrintTask&, PrintAgenda&, WorkListReusability&>
  instruction_handler_visitor_type;

  typedef InstructionVisitor<
  bool, PrintTask&, PrintAgenda&, WorkListReusability&>
  instruction_propagator_visitor_type;

  instruction_handler_visitor_type const& handleInstruction;
  instruction_propagator_visitor_type const& propagateOnUnmarked;

  std::ostream& m_out;
  std::list<VirtualInstruction*> m_markedInstruction;

public:

  PrintAgenda(
      instruction_handler_visitor_type const& handler_visitor,
      instruction_propagator_visitor_type const& propagation_visitor,
      std::ostream& out,
      const Function& function)
    : handleInstruction(handler_visitor)
    , propagateOnUnmarked(propagation_visitor)
    , m_out(out)
  { addNewAsFirst(new PrintTask(function.getFirstInstruction())); }

  virtual ~PrintAgenda(void);

  virtual void markInstructionWith(VirtualInstruction& instruction, VirtualTask& task);
};

} // compilation

// PrintHandler.
namespace compilation
{

template <InstructionType _Type>
struct PrintHandler
    : public InstructionHandler<_Type, PrintTask, PrintAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PrintTask& task,
      PrintAgenda& continuations,
      WorkListReusability& reuse)
  {
    InstructionHandler<_Type, PrintTask, PrintAgenda>
        ::operator ()(instr,task,continuations,reuse);
  }
};

template <>
struct PrintHandler<TEnterBlock>
    : public InstructionHandler<TEnterBlock, PrintTask, PrintAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PrintTask& task,
      PrintAgenda& continuations,
      WorkListReusability& reuse)
  {
    base_handler::operator ()(instr,task,continuations,reuse);
    ++task.m_indent;
  }
};

template <>
struct PrintHandler<TExitBlock>
    : public InstructionHandler<TExitBlock, PrintTask, PrintAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      PrintTask& task,
      PrintAgenda& continuations,
      WorkListReusability& reuse)
  {
    --task.m_indent;
    base_handler::operator ()(instr,task,continuations,reuse);
  }
};

} // compilation

#endif // _COMPILATION_PRINT_TASK_H_
